#include "Util.h"
#include <vector>
#include <string>
#include <fstream>
#include <typeinfo>
#include <stdlib.h>

#include <algorithm> 
#include <functional> 
#include <cctype>
#include <locale>
#include <dirent.h>

#include "Game.h"

#include "Locations/Location.h"
#include "Locations/Indoors.h"
#include "Locations/Outdoors.h"

#include "Characters/Character.h"
#include "Characters/Wizard.h"
#include "Characters/Barbarian.h"
#include "Characters/Paladin.h"

#include "Items/Item.h"
#include "Items/OtherItem.h"
#include "Items/Weapon.h"
#include "Items/Armor.h"

#define VOICE 1

using namespace std;

std::vector<std::string> read(std::vector<cmdalt> alternatives, string message )
{
	speak(message);
	string line;
	getline(cin, line);
	if(line.empty())
	{
		return read(alternatives, message);
	}
	vector<string> words = split(line, ' ');
	for(size_t i=0;i<alternatives.size();i++)
	{
		if(alternatives[i].first.compare(words[0]) == 0)
		{
			if(words.size() > 1 && alternatives[i].second.size()> 0 && alternatives[i].exists(words[1]))
			{
				std::vector<std::string> p;
				p.push_back(words[0]);
				p.push_back(words[1]);
				return p;
			}
			if(words.size() > 2 && alternatives[i].second.size()> 0 && alternatives[i].exists(words[1] + " " + words[2]))
			{
				std::vector<std::string> p;
				p.push_back(words[0]);
				p.push_back(words[1] + " " + words[2]);
				return p;
			}
			if(alternatives[i].second.size() ==  0)
			{
				std::vector<std::string> p;
				p.push_back(words[0]);
				return p;
			}
			break;
		}
		if(words.size() > 1 && alternatives[i].first.compare(words[0] + " " + words[1]) == 0)
		{
			if(words.size() > 2 && alternatives[i].second.size()> 0 && alternatives[i].exists(words[2]))
			{
				std::vector<std::string> p;
				p.push_back(words[0] + " " + words[1]);
				p.push_back(words[2]);
				return p;
			}
			if(words.size() > 3 && alternatives[i].second.size()> 0 && alternatives[i].exists(words[2] + " " + words[3]))
			{
				std::vector<std::string> p;
				p.push_back(words[0] + " " + words[1]);
				p.push_back(words[2] + " " + words[3]);
				return p;
			}
			if(alternatives[i].second.size() ==  0)
			{
				std::vector<std::string> p;
				p.push_back(words[0] + " " + words[1]);
				return p;
			}
			break;
		}
	}
	speak("invalid command");
	return read(alternatives, message);
}

void printAlternatives(std::vector<cmdalt> alt){
	speak("These are your options:");
	for(size_t i = 0; i < alt.size(); i++){
		if(alt[i].second.size() > 0){
			std::cout << alt[i].first << " ";
			if(alt[i].second.size() > 1){
				std::cout << "[ ";
			}
			for(size_t j = 0; j < alt[i].second.size();j++){
				std::cout << alt[i].second[j];
				if(j < alt[i].second.size() - 1){
					std::cout << ", ";
				}
			}
			if(alt[i].second.size() > 1){
				std::cout << " ]";
			}
			std::cout << std::endl;
		}
		else{
			std::cout << alt[i].first << std::endl;
		}
	}
}

void speak(std::string say, bool male, int pitch){
	bool add = true;
	std::string created;
	for(size_t i = 0 ; i<say.size() ; i++)
	{
		if(say[i] == '(')
		{
			add = false;
		}
		if(say[i] == ')')
		{
			add = true;
		}
		if(add)
		{
			created += say[i];
		}
	}
	say = created;
	std::string cmd = "espeak -s 140 -v en+";
	if(male){
		cmd += "m4";
	}
	else{
		cmd += "f4";
	}
	cmd+= " -p ";
	cmd += std::to_string(pitch);
	cmd += " \"";
	cmd += say;
	cmd += "\" 2> /dev/null";
	if(VOICE){
		system(cmd.c_str());
	}
}



void speak(std::string output, Character* speaker){
		
		if(speaker == NULL){
			std::cout  << output << std::endl;
			speak(output, true, 50);
		}
		else{
			std::cout << speaker->name << ": " << output << std::endl;
			speak(output, speaker->getGender(), speaker->getPitch());
		}
}

void speak(std::vector<std::string> input, Character * speaker){
	std::string say;
	for(size_t i = 0 ; i < input.size();i++){
			say += input[i];
			say += " ";
	}
	if(speaker == NULL){
			speak(say, true, 50);
	}
	else{
		speak(say, speaker->getGender(), speaker->getPitch());
	}
}

bool save(Game & g, std::string savename)
{
	if(g.current_loc == NULL)
	{
		std::cout << "No game currently active" << std::endl;
		return false;
	}
	bool fileexists = false;
	std::string saveloc = "saves/";
	std::string savepath = saveloc + savename;
	savepath += ".sav";
	std::string backup = saveloc + savename;
	backup += ".temp";
	if(saveGameExists(savename))
	{
		bool answered = false;
		while(!answered)
		{
			std::string in;
			std::cout << "Do you want to replace saved game with name:  " << savename << "? [y/n]" << std::endl;
			std::cin >> in;
			if(in.compare("y") == 0)
			{
				answered = true;
				fileexists = (rename(savepath.c_str(), backup.c_str()) == 0); // fileexist true if rename succeded. If false we dont want to restore the old version
			}
			if(in.compare("n") == 0)
			{
				answered = false;
				speak("Game not saved");
				return false;
			}
		}
	}
	std::ofstream savestream;
	savestream.open (savepath);
	bool retvalue = false;
	if (savestream.is_open())
	{
		retvalue = saveGame(g, savestream);
		savestream.close();
	}
	else
	{
		cout << "Unable to open file " << endl;
		retvalue = false;
	}
	if(fileexists)
	{
		if(retvalue)
		{
			// delete backup
			remove(backup.c_str());
		}
		else
		{
			// reset backup
			rename(backup.c_str(), savepath.c_str());
		}
	}
	if(!retvalue)
	{
		speak("Whoops this is embarrassing... save failed");
	}
	return retvalue;
}

bool saveGameExists(std::string savename)
{
	vector<string> vec = get_save_files();
	for( size_t i = 0 ; i < vec.size() ; i++ )
	{
		if(vec[i].compare(savename) == 0)
		{
			return true;
		}
	}
	return false;
}

vector<string> get_save_files()
{
	vector<string> vec;
	DIR *dir;
	struct dirent *ent;
	dir = opendir ("saves/");
	if (dir != NULL)
	{
		while ((ent = readdir (dir)) != NULL)
		{
			string name = ent->d_name;
			if(!(name.compare(".") == 0 || name.compare("..") == 0))
			{
				name = name.substr(0,name.size() - 4);
				vec.push_back(name);
			}
		}
		closedir (dir);
	}
	else
	{
	  /* could not open directory */
	  perror ("");
	}
	return vec;
}

bool saveGame(Game &game, std::ofstream &savestream)
{
	savestream << "--GAME--" << endl;
	savestream << "--BEGINSTORY--" << endl;
	savestream << game.begin_story << endl;
	savestream << "--BEGINSTORYDONE--" << endl;
	savestream << "--ENDSTORY--" << endl;
	savestream << game.end_story << endl;;
	savestream << "--ENDSTORYDONE--" << endl;
	savestream << "--MAP--" << endl;
	bool success = true;
	for(size_t i = 0; i < game.locations.size() ; i++)
	{
		Character* pl = NULL;
		if(game.locations[i] == game.current_loc)
		{
			pl = game.player;
		}
		saveLocation(*game.locations[i], savestream, pl);
	}
	savestream << "--MAPDONE--" << endl;
	savestream << "--GAMEDONE--" << endl;
	return success;
}

bool saveLocation(Location &location, std::ofstream &savestream, Character* player)
{
	savestream << "--LOCATION--" << endl;
	bool success = saveLocationExtras(location, savestream);
	savestream << "NAME:" << location.loc_name << endl;
	savestream << "--DESCRIPTION--" << endl;
	savestream << location.loc_description << endl;
	savestream << "--DESCRIPTIONDONE--" << endl;
	savestream << "VISITED:";
	if(location.loc_visited)
	{
		savestream << "1";
	}
	else
	{
		savestream << "0";
	}
	savestream << endl;
	savestream << "--ITEMS--" << endl;
	for(size_t i = 0 ; i < location.items.size() ; i++)
	{
		success = saveItem(*location.items[i], savestream);
		if(!success)
		{
			return success;
		}
	}	
	savestream << "--ITEMSDONE--" << endl;
	if(player != NULL)
	{
		savestream << "--PLAYER--" << endl;
		success = saveCharacter(*player, savestream);
		savestream << "--PLAYERDONE--" << endl;
	}
	savestream << "--CHARACTERS--" << endl;
	for(size_t i = 0 ; i < location.loc_characters.size() ; i++)
	{
		success = saveCharacter(*location.loc_characters[i], savestream);
		if(!success)
		{
			return success;
		}
	}
	savestream << "--CHARACTERSDONE--" << endl;
	savestream << "--NEIGHBORS--" << endl;
	for(size_t i = 0 ; i < location.loc_neighbors.size() ; i++)
	{
		savestream << location.loc_neighbors[i].direction << ":" << location.loc_neighbors[i].location->get_name() << ":"  << location.loc_neighbors[i].required << endl;
	}
	savestream << "--NEIGHBORSDONE--" << endl;
	savestream << "--LOCATIONDONE--" << endl;;
	return success;
}

bool saveLocationExtras(Indoors & indoors, ofstream &savestream)
{
	savestream << "TYPE:Indoors" << endl;
	return true;
}

bool saveLocationExtras(Outdoors & outdoors, ofstream &savestream)
{
	savestream << "TYPE:Outdoors" << endl;
	return true;
}

bool saveLocationExtras(Location & location, ofstream &savestream)
{
	bool success = true;
	bool castsuccess = false;
	if(!castsuccess){
		try
		{
			Indoors & indoors = dynamic_cast<Indoors&>(location);
			success = saveLocationExtras(indoors, savestream);
			castsuccess = true;
		}
		catch(std::bad_cast exp)
		{
		}
	}
	if(!castsuccess){
		try
		{
			Outdoors & outdoors = dynamic_cast<Outdoors&>(location);
			success = saveLocationExtras(outdoors, savestream);
			castsuccess = true;
		}
		catch(std::bad_cast exp)
		{
		}
	}
	if(!castsuccess)
	{
		return false;
	}
	return success;
}

bool saveCharacter(Character & character, ofstream &savestream)
{
	savestream << "--CHARACTER--" << endl;
	bool success = saveCharacterExtras(character, savestream);
	savestream << "NAME:" << character.name << endl;
	savestream << "RACE:" << character.race << endl;
	savestream << "HP:" << character.HP << endl;
	savestream << "MAXHP:" << character.maxHP << endl;
	savestream << "EXP:" << character.exp << endl;
	savestream << "DEF:" << character.def << endl;
	savestream << "MAGDEF:" << character.magDef << endl;
	savestream << "LEVEL:" << character.level << endl;
	savestream << "MALE:" << character.male << endl;
	savestream << "HOSTILE:" << character.hostile << endl;
	savestream << "PITCH:" << character.pitch << endl;
	savestream << "--ITEMS--" << endl;
	if(character.equipment.weapon != NULL)
	{
		success = saveItem(*character.equipment.weapon, savestream);
	}
	if(character.equipment.armor != NULL)
	{
		success = saveItem(*character.equipment.armor, savestream);
	}
	if(character.equipment.other1 != NULL)
	{
		success = saveItem(*character.equipment.other1, savestream);
	}
	if(character.equipment.other2 != NULL)
	{
		success = saveItem(*character.equipment.other1, savestream);
	}
	if(character.equipment.other3 != NULL)
	{
		success = saveItem(*character.equipment.other1, savestream);
	}
	savestream << "--ITEMSDONE--" << endl;
	success = saveAchievement(character.kill, savestream);
	savestream << "--TRASHTALK--" << endl;
	for(size_t i = 0; i<character.trashTalk.size() ; i++)
	{
		savestream << character.trashTalk[i] << endl;
	}
	savestream << "--TRASHTALKDONE--" << endl;
	savestream << "--DIALOGS--" << endl;
	for(size_t i = 0; i<character.dialogs.size() ; i++)
	{
		success = saveDialog(character.dialogs[i], savestream);
	}
	savestream << "--DIALOGSDONE--" << endl;
	savestream << "--PROGRESS--" << endl;
	success = saveProgress(*character.prog, savestream);
	savestream << "--PROGRESSDONE--" << endl;
	savestream << "--CHARACTERDONE--" << endl;
	return success;
}

bool saveProgress(Progress & prog, ofstream & savestream)
{
	for(size_t i = 0; i< prog.prog.size() ; i++)
	{
		savestream << prog.prog[i] << endl;
	}
	return true;
}

bool saveDialog(dialog & d, ofstream & savestream)
{
	bool success = true;
	savestream << "--DIALOG--" << endl;
	savestream << "DIALOG:" << d.dia << endl;
	savestream << "ACHIEVEMENTPROGRESS:" << d.achievement.p << endl;
	savestream << "ACHIEVEMENTEXP:" << d.achievement.xp << endl;
	savestream << "--REQPROGRESS--" << endl;
	for(size_t i = 0; i<d.required.size() ; i++)
	{
		savestream << d.required[i].p << ":" << d.required[i].modifier << endl;
	}
	savestream << "--REQPROGRESSDONE--" << endl;
	if(d.achievement.p != NONE)
	{
		success = saveAchievement(d.achievement, savestream);
	}
	savestream << "--DIALOGDONE--" << endl;
	return success;
}

bool saveAchievement(Progress::ach& a, ofstream & savestream)
{
	savestream << "--ACHIEVEMENT--" << endl;
	savestream << "PROGRESS:" << a.p << endl;
	savestream << "EXP:" << a.xp << endl;
	bool success = true;
	if(a.item != NULL)
	{
		success = saveItem(*a.item, savestream);
	}
	savestream << "--ACHIEVEMENTDONE--" << endl;
	return success;
}

bool saveCharacterExtras(Character & character, ofstream &savestream)
{
	bool success = true;
	bool castsuccess = false;
	if(!castsuccess){
		try
		{
			Wizard & wizard = dynamic_cast<Wizard&>(character);
			success = saveCharacterExtras(wizard, savestream);
			castsuccess = true;
		}
		catch(std::bad_cast exp)
		{
		}
	}
	if(!castsuccess){
		try
		{
			Paladin & paladin = dynamic_cast<Paladin&>(character);
			success = saveCharacterExtras(paladin, savestream);
			castsuccess = true;
		}
		catch(std::bad_cast exp)
		{
		}
	}
	if(!castsuccess){
		try
		{
			Barbarian & barbarian = dynamic_cast<Barbarian&>(character);
			success = saveCharacterExtras(barbarian, savestream);
			castsuccess = true;
		}
		catch(std::bad_cast exp)
		{
		}
	}
	if(!castsuccess)
	{
		return false;
	}
	return success;
}

bool saveCharacterExtras(Wizard & wizard, ofstream &savestream)
{
	savestream << "TYPE:Wizard" << endl;
	savestream << "MANA:" << wizard.mana << endl;
	savestream << "MAXMANA:" << wizard.maxMana << endl;
	savestream << "MAG:" << wizard.mag << endl;
	return true;
}

bool saveCharacterExtras(Paladin & paladin, ofstream &savestream)
{
	savestream << "TYPE:Paladin" << endl;
	savestream << "MANA:" << paladin.mana << endl;
	savestream << "MAXMANA:" << paladin.maxMana << endl;
	savestream << "MAG:" << paladin.mag << endl;
	savestream << "STAMINA:" << paladin.stamina << endl;
	savestream << "MAXSTAMINA:" << paladin.maxStamina << endl;
	savestream << "ATK:" << paladin.atk << endl;
	return true;
}

bool saveCharacterExtras(Barbarian & barbarian, ofstream &savestream)
{
	savestream << "TYPE:Barbarian" << endl;
	savestream << "STAMINA:" << barbarian.stamina << endl;
	savestream << "MAXSTAMINA:" << barbarian.maxStamina << endl;
	savestream << "ATK:" << barbarian.atk << endl;
	return true;
}

bool saveItem(Item & item, ofstream &savestream)
{
	savestream << "--ITEM--" << endl;
	bool success = saveItemExtras(item, savestream);
	savestream << "NAME:" << item.name << endl;
	savestream << "HP:" << item.maxHP << endl;
	savestream << "STAMINA:" << item.maxStamina << endl;
	savestream << "MANA:" << item.maxMana << endl;
	savestream << "MAGDEF:" << item.magDef << endl;
	savestream << "--ITEMDONE--" << endl;
	return success;
}

bool saveItemExtras(Item & item, ofstream &savestream)
{
	bool success = true;
	bool castsuccess = false;
	if(!castsuccess)
	{
		try
		{
			OtherItem & otheritem = dynamic_cast<OtherItem&>(item);
			success = saveItemExtras(otheritem, savestream);
			castsuccess = true;
		}
		catch(std::bad_cast exp)
		{
		}
	}
	if(!castsuccess){
		try
		{
			Weapon & weapon = dynamic_cast<Weapon&>(item);
			success = saveItemExtras(weapon, savestream);
			castsuccess = true;
		}
		catch(std::bad_cast exp)
		{
		}
	}
	if(!castsuccess){
		try
		{
			Armor & armor = dynamic_cast<Armor&>(item);
			success = saveItemExtras(armor, savestream);
			castsuccess = true;
		}
		catch(std::bad_cast exp)
		{
		}
	}
	if(!castsuccess)
	{
		return false;
	}
	return success;
}

bool saveItemExtras(OtherItem & item, ofstream &savestream)
{
	savestream << "TYPE:OtherItem" << endl;
	savestream << "ATK:" << item.atk << endl;
	savestream << "DEF:" << item.def << endl;
	savestream << "MAG:" << item.mag << endl;
	return true;
}

bool saveItemExtras(Weapon & item, ofstream &savestream)
{
	savestream << "TYPE:Weapon" << endl;
	savestream << "ATK:" << item.atk << endl;
	savestream << "MAG:" << item.mag << endl;
	return true;
}

bool saveItemExtras(Armor & item, ofstream &savestream)
{
	savestream << "TYPE:Armor" << endl;
	savestream << "DEF:" << item.def << endl;
	return true;
}

bool load(Game &g, std::string savename)
{
	if(!saveGameExists(savename))
	{
		std::cout << "Saved game does not exist" << std::endl;
		return false;
	}
	string line;
	savename = string("saves/") + savename + ".sav";
	ifstream input (savename);
	bool success = false;
	if (input.is_open())
	{
		while ( input.good() )
		{
			getline (input,line);
			bool cmp = line.compare("--GAME--") == 0;
			if(cmp)
			{
				success = loadGame(g, input);
			}
			else if(!line.empty())
			{
				std::cout << "Util.load Line not read: " << line << endl;
			}
		}
		input.close();
	}
	else cout << "Unable to open file " << endl;
	return success;
}

bool loadGame(Game &g, ifstream & input)
{
	string line;
	while ( input.good() )
	{
		getline (input,line);
		if(line.compare("--GAMEDONE--") == 0)
		{
			return true;
		}
		else if(line.compare("--BEGINSTORY--") == 0)
		{
			std::string story = "";
			while ( input.good() )
			{
				getline (input,line);
				if(line.compare("--BEGINSTORYDONE--") == 0)
				{
					story = story.substr(0,story.size()-1);
					g.begin_story = story;
					break;
				}
				story += line;
				story += "\n";
			}
		}
		else if(line.compare("--ENDSTORY--") == 0)
		{
			std::string story = "";
			while ( input.good() )
			{
				getline (input,line);
				if(line.compare("--ENDSTORYDONE--") == 0)
				{
					story = story.substr(0,story.size()-1);
					g.end_story = story;
					break;
				}
				story += line;
				story += "\n";
			}
		}
		else if(line.compare("--MAP--") == 0)
		{
			while ( input.good() )
			{
				getline (input,line);
				if(line.compare("--MAPDONE--") == 0)
				{
					break;
				}
				else if(line.compare("--LOCATION--") == 0)
				{
					Location* loc = loadLocation(g, input);
					if(loc == NULL)
					{
						cout << "Reading Location failed" << endl;
						return false;
					}
					g.locations.push_back(loc);
				}
				else if(!line.empty())
				{
					std::cout << "Util.loadGame(MAP) Line not read: " << line << endl;
				}
			}
		}
		else if(!line.empty())
		{
			std::cout << "Util.loadGame Line not read: " << line << endl;
		}
	}
	return false;
}

Location* loadLocation(Game &g, ifstream & input)
{
	std::string type = "TYPE:";
	std::string instancetype = "";
	std::string name = "NAME:";
	std::string visited = "VISITED:";
	
	Location* loc = NULL;
	string line = "";
	while ( input.good() )
	{
		getline (input,line);
		if(line.compare("--LOCATIONDONE--") == 0)
		{
			return loc;
		}
		else if(line.substr(0,type.size()) == type)
		{
			if(line.substr(type.size(),line.size()) == "Indoors")
			{
				loc = new Indoors(g.player, "", "");
				instancetype = "Indoors";
			}
			if(line.substr(type.size(),line.size()) == "Outdoors")
			{
				loc = new Outdoors(g.player, "", "");
				instancetype = "Indoors";
			}
			loc->player = NULL;
		}
		else if(line.substr(0,name.size()) == name){
			loc->loc_name = line.substr(name.size(),line.size());
		}
		else if(line.substr(0,visited.size()) == visited){
			int vis = atoi(line.substr(visited.size(),line.size()).c_str());
			if(vis)
			{
				loc->loc_visited = true;
			}
			else
			{
				loc->loc_visited = false;
			}
		}
		else if(line.compare("--DESCRIPTION--") == 0)
		{
			std::string description  = "";
			while ( input.good() )
			{
				getline (input,line);
				if(line.compare("--DESCRIPTIONDONE--") == 0)
				{
					if(loc == NULL)
					{
						cout << "location NULL when adding description" << endl;
						return NULL;					
					}
					description = description.substr(0,description.size()-1);
					loc->loc_description = description;
					break;
				}
				
				description += line;
				description += "\n";
			}
		}
		else if(line.compare("--CHARACTERS--") == 0)
		{
			while ( input.good() )
			{
				getline (input,line);
				if(line.compare("--CHARACTERSDONE--") == 0)
				{
					break;
				}
				else if(line.compare("--CHARACTER--") == 0)
				{
					Character* character = loadCharacter(input);
					if(loc == NULL)
					{
						cout << "location NULL when adding character" << endl;
						if(character == NULL)
						{
							delete character;
						}
						return NULL;
					}
					if(character == NULL)
					{
						cout << "Location: " << loc->loc_name << endl;
						cout << "character NULL when adding to location" << endl;
						delete loc;
						return NULL;
					}
					loc->loc_characters.push_back(character);
				}
				else if(!line.empty())
				{
					std::cout << "Util.loadLocation(CHARACTERS) Line not read: " << line << endl;
				}
			}
		}
		else if(line.compare("--ITEMS--") == 0)
		{
			while ( input.good() )
			{
				getline (input,line);
				if(line.compare("--ITEMSDONE--") == 0)
				{
					break;
				}
				else if(line.compare("--ITEM--") == 0)
				{
					Item* item = loadItem(input);
					if(loc == NULL)
					{
						cout << "location NULL when adding item" << endl;
						if(item == NULL)
						{
							delete item;
						}
						return NULL;
					}
					if(item == NULL)
					{
						cout << "item NULL when adding to location" << endl;
						delete loc;
						return NULL;
					}
					loc->items.push_back(item);
				}
				else if(!line.empty())
				{
					std::cout << "Util.loadLocation(ITEMS) Line not read: " << line << endl;
				}
			}
		}
		else if(line.compare("--PLAYER--") == 0)
		{
			while ( input.good() )
			{
				getline (input,line);
				if(line.compare("--PLAYERDONE--") == 0)
				{
					break;
				}
				else if(line.compare("--CHARACTER--") == 0)
				{
					Character* player = loadCharacter(input);
					if(loc == NULL)
					{
						cout << "location NULL when adding player" << endl;
						if(player == NULL)
						{
							delete player;
						}
						return NULL;
					}
					if(player == NULL)
					{
						cout << "player NULL when adding to location" << endl;
						delete loc;
						return NULL;
					}
					g.player = player;
					loc->player = player;
					g.current_loc = loc;
				}
				else if(!line.empty())
				{
					std::cout << "Util.loadLocation(PLAYER) Line not read: " << line << endl;
				}
			}
		}
		else if(line.compare("--NEIGHBORS--") == 0)
		{
			while ( input.good() )
			{
				getline (input,line);
				if(line.compare("--NEIGHBORSDONE--") == 0)
				{
					break;
				}
				else
				{
					vector<string> neighbor_list = split(line,':');
					//string part1 = neighbor_list[0];
					//string part2 = neighbor_list[1];
					bool exists = false;
					for(size_t i=0 ; i < g.locations.size() ; i++)
					{
						if(g.locations[i]->loc_name.compare(neighbor_list[1]) == 0)
						{
							loc->add_neighbor(neighbor_list[0], g.locations[i], (progress)atoi(neighbor_list[2].c_str()));
							for(size_t j=0 ; j < g.locations[i]->loc_neighbors.size() ; j++)
							{
								string tempname = string("##") + loc->loc_name + string("##");
								Location * temp = g.locations[i]->loc_neighbors[j].location;
								if(temp->loc_name.compare(tempname) == 0)
								{
									g.locations[i]->loc_neighbors[j].location = loc;
									delete temp;
								}
							}
							exists = true;
							break;
						}
					}
					if(!exists)
					{
						Location* neighborpointer = new Indoors(g.player, "", "");
						neighborpointer->loc_name = string("##") + neighbor_list[1] + string("##");
						loc->add_neighbor(neighbor_list[0], neighborpointer,(progress)atoi(neighbor_list[2].c_str()));
					}
				}
			}
		}
		else if(!line.empty())
		{
			std::cout << "Util.loadLocation Line not read: " << line << endl;
		}
	}
	delete loc;
	return NULL;
}

Item* loadItem(ifstream & input)
{
	string type = "TYPE:";
	string hp = "HP:";
	string mana = "MANA:";
	string magdef = "MAGDEF:";
	string stamina = "STAMINA:";
	string name = "NAME:";
	
	std::string line;
	Item* item = NULL;
	while ( input.good() )
	{
		getline (input,line);
		if(line.compare("--ITEMDONE--") == 0)
		{
			return item;
		}
		else if(line.substr(0,type.size()) == type)
		{
			if(line.substr(type.size(),line.size()) == "OtherItem")
			{
				OtherItem* other = new OtherItem();
				bool atk = false;
				bool def = false;
				bool mgi = false;
				string attack = "ATK:";
				string defence = "DEF:";
				string magi = "MAG:";
				while(input.good() && (!atk || !def || !mgi))
				{
					getline (input,line);
					if(line.substr(0,attack.size()) == attack)
					{
						other->atk = atoi(line.substr(attack.size(),line.size()).c_str());
						atk = true;
					}
					else if(line.substr(0,defence.size()) == defence)
					{
						other->def = atoi(line.substr(defence.size(),line.size()).c_str());
						def = true;
					}
					else if(line.substr(0,magi.size()) == magi)
					{
						other->mag = atoi(line.substr(magi.size(),line.size()).c_str());
						mgi = true;
					}
					else if(!line.empty())
					{
						std::cout << "Util.loadItem(OtherItem) Line not read: " << line << endl;
					}
				}
				item = other;
			}
			if(line.substr(type.size(),line.size()) == "Weapon")
			{
				Weapon* weapon = new Weapon();
				bool atk = false;
				bool mgi = false;
				string attack = "ATK:";
				string magi = "MAG:";
				while(input.good() && (!atk || !mgi))
				{
					getline (input,line);
					if(line.substr(0,attack.size()) == attack)
					{
						weapon->atk = atoi(line.substr(attack.size(),line.size()).c_str());
						atk = true;
					}
					else if(line.substr(0,magi.size()) == magi)
					{
						weapon->mag = atoi(line.substr(magi.size(),line.size()).c_str());
						mgi = true;
					}
					else if(!line.empty())
					{
						std::cout << "Util.loadItem(Weapon) Line not read: " << line << endl;
					}
				}
				item = weapon;
			}
			if(line.substr(type.size(),line.size()) == "Armor")
			{
				Armor* armor = new Armor();
				bool def = false;
				string defence = "DEF:";
				while(input.good() && !def)
				{
					getline (input,line);
					if(line.substr(0,defence.size()) == defence)
					{
						armor->def = atoi(line.substr(defence.size(),line.size()).c_str());
						def = true;
					}
					else if(!line.empty())
					{
						std::cout << "Util.loadItem(Armor) Line not read: " << line << endl;
					}
				}
				item = armor;
			}
		}
		else if(line.substr(0,hp.size()) == hp)
		{
			item->maxHP = atoi(line.substr(hp.size(),line.size()).c_str());
		}
		else if(line.substr(0,stamina.size()) == stamina)
		{
			item->maxStamina = atoi(line.substr(stamina.size(),line.size()).c_str());
		}
		else if(line.substr(0,magdef.size()) == magdef)
		{
			item->magDef = atoi(line.substr(magdef.size(),line.size()).c_str());
		}
		else if(line.substr(0,mana.size()) == mana)
		{
			item->maxMana = atoi(line.substr(mana.size(),line.size()).c_str());
		}
		else if(line.substr(0,name.size()) == name)
		{
			item->name = line.substr(name.size(),line.size());
		}
		else if(!line.empty())
		{
			std::cout << "Util.loadItem Line not read: " << line << endl;
		}
	}
	delete item;
	return NULL;
}

Character* loadCharacter(ifstream & input)
{
	string type = "TYPE:";
	string name = "NAME:";
	string hp = "HP:";
	string maxhp = "MAXHP:";
	string exp = "EXP:";
	string def = "DEF:";
	string magdef = "MAGDEF:";
	string level = "LEVEL:";
	string male = "MALE:";
	string hostile = "HOSTILE:";
	string pitch = "PITCH:";
	string race = "RACE:";
	
	Character* character = NULL;
	string line = "";
	while ( input.good() )
	{
		getline (input,line);
		if(line.compare("--CHARACTERDONE--") == 0)
		{
			return character;
		}
		else if(line.substr(0,type.size()) == type)
		{
			if(line.substr(type.size(),line.size()) == "Paladin")
			{
				Paladin* paladin = new Paladin();
				bool atk = false;
				bool mgi = false;
				bool sta = false;
				bool man = false;
				bool maxsta = false;
				bool maxman = false;
				string attack = "ATK:";
				string magi = "MAG:";
				string mana = "MANA:";
				string maxmana = "MAXMANA:";
				string stamina = "STAMINA:";
				string maxstamina = "MAXSTAMINA:";
				while(input.good() && (!atk || !mgi || !man || !sta || !maxsta || !maxman))
				{
					getline (input,line);
					if(line.substr(0,attack.size()) == attack)
					{
						paladin->atk = atoi(line.substr(attack.size(),line.size()).c_str());
						atk = true;
					}
					else if(line.substr(0,magi.size()) == magi)
					{
						paladin->mag = atoi(line.substr(magi.size(),line.size()).c_str());
						mgi = true;
					}
					else if(line.substr(0,mana.size()) == mana)
					{
						paladin->mana = atoi(line.substr(mana.size(),line.size()).c_str());
						man = true;
					}
					else if(line.substr(0,maxmana.size()) == maxmana)
					{
						paladin->maxMana = atoi(line.substr(maxmana.size(),line.size()).c_str());
						maxman = true;
					}
					else if(line.substr(0,stamina.size()) == stamina)
					{
						paladin->stamina = atoi(line.substr(stamina.size(),line.size()).c_str());
						sta = true;
					}
					else if(line.substr(0,maxstamina.size()) == maxstamina)
					{
						paladin->maxStamina = atoi(line.substr(maxstamina.size(),line.size()).c_str());
						maxsta = true;
					}
					else if(!line.empty())
					{
						std::cout << "Util.loadCharacter(Paladin) Line not read: " << line << endl;
					}
				}
				character = paladin;
			}
			if(line.substr(type.size(),line.size()) == "Wizard")
			{
				Wizard* wizard = new Wizard();
				bool mgi = false;
				bool man = false;
				bool maxman = false;
				string magi = "MAG:";
				string mana = "MANA:";
				string maxmana = "MAXMANA:";
				while(input.good() && (!mgi || !man || !maxman))
				{
					getline (input,line);
					if(line.substr(0,magi.size()) == magi)
					{
						wizard->mag = atoi(line.substr(magi.size(),line.size()).c_str());
						mgi = true;
					}
					else if(line.substr(0,mana.size()) == mana)
					{
						wizard->mana = atoi(line.substr(mana.size(),line.size()).c_str());
						man = true;
					}
					else if(line.substr(0,maxmana.size()) == maxmana)
					{
						wizard->maxMana = atoi(line.substr(maxmana.size(),line.size()).c_str());
						maxman = true;
					}
					else if(!line.empty())
					{
						std::cout << "Util.loadCharacter Line not read: " << line << endl;
					}
				}
				character = wizard;
			}
			if(line.substr(type.size(),line.size()) == "Barbarian")
			{
				Barbarian* barbarian = new Barbarian();
				bool atk = false;
				bool sta = false;
				bool maxsta = false;
				string attack = "ATK:";
				string stamina = "STAMINA:";
				string maxstamina = "MAXSTAMINA:";
				while(input.good() && (!atk || !sta || !maxsta ))
				{
					getline (input,line);
					if(line.substr(0,attack.size()) == attack)
					{
						barbarian->atk = atoi(line.substr(attack.size(),line.size()).c_str());
						atk = true;
					}
					else if(line.substr(0,stamina.size()) == stamina)
					{
						barbarian->stamina = atoi(line.substr(stamina.size(),line.size()).c_str());
						sta = true;
					}
					else if(line.substr(0,maxstamina.size()) == maxstamina)
					{
						barbarian->maxStamina = atoi(line.substr(maxstamina.size(),line.size()).c_str());
						maxsta = true;
					}
					else if(!line.empty())
					{
						std::cout << "Util.loadCharacter Line not read: " << line << endl;
					}
				}
				character = barbarian;
			}
		}
		else if(line.compare("--TRASHTALK--") == 0)
		{
			vector<string> trash;
			if(character == NULL)
			{
				cout << "character NULL when adding trashtalk" << endl;
				return NULL;					
			}
			std::string description  = "";
			while ( input.good() )
			{
				getline (input,line);
				if(line.compare("--TRASHTALKDONE--") == 0)
				{
					character->setTrashTalk(trash);
					break;
				}
				else
				{
					trash.push_back(line);
				}
			}
		}
		else if(line.compare("--DIALOGS--") == 0)
		{
			vector<dialog> dialogs;
			while ( input.good() )
			{
				getline (input,line);
				if(line.compare("--DIALOGSDONE--") == 0)
				{
					character->setDialogs(dialogs);
					break;
				}
				else if(line.compare("--DIALOG--") == 0)
				{
					if(!loadDialog(dialogs, input))
					{
						cout << "loadDialog failed" << endl;
					}
				}
				else if(!line.empty())
				{
					std::cout << "Util.loadCharacter(DIALOGS) Line not read: " << line << endl;
				}
			}
		}
		else if(line.compare("--ACHIEVEMENT--") == 0)
		{
			if(character == NULL)
			{
				return NULL;
			}
			bool success = loadAchievement(character->kill, input);
			if(!success)
			{
				delete character;
				return NULL;
			}
		}
		else if(line.substr(0,name.size()) == name)
		{
			character->name = line.substr(name.size(),line.size());
		}
		else if(line.substr(0,race.size()) == race)
		{
			character->race = line.substr(race.size(),line.size());
		}
		else if(line.substr(0,hostile.size()) == hostile)
		{
			character->hostile = atoi(line.substr(hostile.size(),line.size()).c_str());
		}
		else if(line.substr(0,pitch.size()) == pitch)
		{
			character->pitch = atoi(line.substr(pitch.size(),line.size()).c_str());
		}
		else if(line.substr(0,male.size()) == male)
		{
			if(atoi(line.substr(male.size(),line.size()).c_str()) == 1)
			{
				character->male = true;
			}
			else
			{
				character->male = false;
			}
		}
		else if(line.substr(0,level.size()) == level)
		{
			character->level = atoi(line.substr(level.size(),line.size()).c_str());
		}
		else if(line.substr(0,def.size()) == def)
		{
			character->def = atoi(line.substr(def.size(),line.size()).c_str());
		}
		else if(line.substr(0,magdef.size()) == magdef)
		{
			character->magDef = atoi(line.substr(magdef.size(),line.size()).c_str());
		}
		else if(line.substr(0,exp.size()) == exp)
		{
			character->exp = atoi(line.substr(exp.size(),line.size()).c_str());
		}
		else if(line.substr(0,maxhp.size()) == maxhp)
		{
			character->maxHP = atoi(line.substr(maxhp.size(),line.size()).c_str());
		}
		else if(line.substr(0,hp.size()) == hp)
		{
			character->HP = atoi(line.substr(hp.size(),line.size()).c_str());
		}
		else if(line.compare("--ITEMS--") == 0)
		{
			while ( input.good() )
			{
				getline (input,line);
				if(line.compare("--ITEMSDONE--") == 0)
				{
					break;
				}
				else if(line.compare("--ITEM--") == 0)
				{
					Item* item = loadItem(input);
					if(character == NULL)
					{
						cout << "character NULL when adding item" << endl;
						return false;
					}
					if(item == NULL)
					{
						cout << "item NULL when adding to character" << endl;
						return false;
					}
					character->equipItem(*item, false);
				}
			}
		}
		else if(line.compare("--PROGRESS--") == 0)
		{
			Progress * p = loadProgress(input);
			if(character == NULL)
			{
				cout << "character NULL when adding progress" << endl;
				if(p == NULL)
				{
					delete p;
				}
				return NULL;					
			}
			if(p == NULL)
			{
				cout << "progress NULL when adding progress" << endl;
				delete p;
				return NULL;					
			}
			character->prog = p;
		}
		else if(!line.empty())
		{
			std::cout << "Util.loadCharacter Line not read: " << line << endl;
		}
	}
	delete character;
	return NULL;
}

Progress* loadProgress(ifstream& input)
{
	Progress * prog = new Progress();
	string line;
	while ( input.good() )
	{
		getline (input,line);
		if(line.compare("--PROGRESSDONE--") == 0)
		{
			return prog;
		}
		else
		{
			progress p = (progress)atoi(line.c_str());
			prog->prog.push_back(p);
		}
	}
	delete prog;
	return NULL;
}

bool loadDialog(vector<dialog> & dialogs, ifstream & input)
{
	vector<dialog::req> load_req;
	string load_dia;
	Progress::ach achievement = NONE;
	
	string line = "";
	std::string dial = "DIALOG:";
	while ( input.good() )
	{
		getline (input,line);
		if(line.compare("--DIALOGDONE--") == 0)
		{
			dialogs.push_back(dialog(load_req , load_dia, achievement));
			return true;
		}
		else if(line.substr(0,dial.size()) == dial)
		{
			load_dia = line.substr(dial.size(),line.size());
		}
		else if(line.compare("--ACHIEVEMENT--") == 0)
		{
			bool success = loadAchievement(achievement, input);
			if(!success)
			{
				return NULL;
			}
		}
		else if(line.compare("--REQPROGRESS--") == 0)
		{
			while ( input.good() )
			{
				getline (input,line);
				if(line.compare("--REQPROGRESSDONE--") == 0)
				{
					break;
				}
				else
				{
					dialog::req r;
					std::vector<std::string> requirement = split(line, ':');
					r.p = (progress)atoi(requirement[0].c_str());
					if(atoi(requirement[1].c_str()) == 0)
					{
						r.modifier = false;
					}
					else
					{
						r.modifier = true;
					}
					load_req.push_back(r);
				}
			}
		}
	}
	return false;
}

bool loadAchievement(Progress::ach& a, ifstream & input)
{
	string line = "";
	std::string prog = "PROGRESS:";
	std::string exp = "EXP:";
	
	while ( input.good() )
	{
		getline (input,line);
		if(line.compare("--ACHIEVEMENTDONE--") == 0)
		{
			return true;
		}
		else if(line.substr(0,prog.size()) == prog)
		{
			a.p = (progress)atoi(line.substr(prog.size(),line.size()).c_str());
		}
		else if(line.substr(0,exp.size()) == exp)
		{
			a.xp = atoi(line.substr(exp.size(),line.size()).c_str());
		}
		else if(line.compare("--ITEM--") == 0)
		{
			Item* temp = loadItem(input);
			if(temp == NULL)
			{
				return false;
			}
			a.item = temp;
		}
		else if(!line.empty())
		{
			cout << "line not read loadAchievement " << line << endl;
		}
	}
	return false;
}

std::vector<std::string> split(const std::string &s, char delim) {
	std::vector<std::string> elems;
    std::stringstream ss(s);
    std::string item;
    while(std::getline(ss, item, delim)) {
        elems.push_back(item);
    }
    return elems;
}
